﻿using System;
using System.Threading.Tasks;

using System.Configuration;
using System.Web.Configuration;

using COSXML;
using COSXML.Auth;
using COSXML.Model.Object;
using COSXML.Model.Bucket;
using COSXML.CosException;
using System.Collections.Generic;
using COSXML.Model.Service;
using COSXML.Model.Tag;
using COSXML.Transfer;
using COSXML.Model;
using COSXML.Utils;
using System.IO;
using Newtonsoft.Json.Linq;
using TecentCOS.Models;
using System.Net.Http;
using System.Net;
using System.Text;
using System.Text.Encodings.Web;
using System.Web.Script.Serialization;
using Newtonsoft.Json;
using System.Linq;
using static Db.Enums.BlindWaterMark;
using TecentCOS.Configs;
using Db.Models.Base;

namespace TecentCOS
{

    public class TecentCosService
    {

        private TecentCosService()
        {

        }
        private static TecentCosService instance;
        public static TecentCosService Instance()
        {
            if (instance == null) instance = new TecentCosService();
            return instance;

        }

        public CosXml InitServer()
        {
            //设置默认 HTTPS 请求
            CosXmlConfig config = new CosXmlConfig.Builder().IsHttps(false)
                .SetAppid(CosConfig.appid)
                .SetRegion("ap-beijing")  //设置一个默认的存储桶地域
                .SetDebugLog(true)  //显示日志
                    .Build();  //创建 CosXmlConfig 对象
            QCloudCredentialProvider cosCredentialProvider = new DefaultQCloudCredentialProvider(
  CosConfig.SecretId, CosConfig.SecretKey, CosConfig.durationSecond);

            CosXml cosXml = new CosXmlServer(config, cosCredentialProvider);
            return cosXml;
        }

        public List<ListAllMyBuckets.Bucket> GetBuckets()
        {
            try
            {
                var cosXml = InitServer();
                GetServiceRequest request = new GetServiceRequest();
                //执行请求
                GetServiceResult result = cosXml.GetService(request);
                //得到所有的 buckets
                List<ListAllMyBuckets.Bucket> allBuckets = result.listAllMyBuckets.buckets;

                return allBuckets;
            }
            catch (COSXML.CosException.CosClientException clientEx)
            {
                //请求失败
                Console.WriteLine("CosClientException: " + clientEx);
                return null;
            }
            catch (COSXML.CosException.CosServerException serverEx)
            {
                //请求失败
                Console.WriteLine("CosServerException: " + serverEx.GetInfo());
                return null;
            }
        }


        public ListBucket GetListFromBucket(string marker, string limit = "9")
        {
            try
            {
                var cosXml = InitServer();
                string bucket = CosConfig.BucketName; //格式：BucketName-APPID
                GetBucketRequest request = new GetBucketRequest(bucket);
                if (!string.IsNullOrEmpty(marker)) request.SetMarker(marker);

                request.SetMaxKeys(limit);

                //执行请求
                GetBucketResult result = cosXml.GetBucket(request);
                //bucket的相关信息
                ListBucket info = result.listBucket;
                if (info.isTruncated)
                {

                }
                return info;
            }
            catch (COSXML.CosException.CosClientException clientEx)
            {
                //请求失败
                Console.WriteLine("CosClientException: " + clientEx);
                return null;
            }
            catch (COSXML.CosException.CosServerException serverEx)
            {
                //请求失败
                Console.WriteLine("CosServerException: " + serverEx.GetInfo());
                return null;

            }
        }



        public ListBucket GetListFromBucket(string marker, string prefix, string limit = "9")
        {
            try
            {
                var cosXml = InitServer();
                string bucket = CosConfig.BucketName; //格式：BucketName-APPID
                GetBucketRequest request = new GetBucketRequest(bucket);
                if (!string.IsNullOrEmpty(marker)) request.SetMarker(marker);

                request.SetMaxKeys(limit);
                request.SetPrefix(prefix);
                //执行请求
                GetBucketResult result = cosXml.GetBucket(request);
                //bucket的相关信息
                ListBucket info = result.listBucket;
                if (info.isTruncated)
                {

                }
                return info;
            }
            catch (COSXML.CosException.CosClientException clientEx)
            {
                //请求失败
                Console.WriteLine("CosClientException: " + clientEx);
                return null;
            }
            catch (COSXML.CosException.CosServerException serverEx)
            {
                //请求失败
                Console.WriteLine("CosServerException: " + serverEx.GetInfo());
                return null;

            }
        }
        public string GeneratePreviewUrl(string objectKey)
        {
            try
            {
                PreSignatureStruct preSignatureStruct = new PreSignatureStruct();
                preSignatureStruct.appid = CosConfig.appid;//腾讯云账号 APPID
                preSignatureStruct.region = "ap-beijing"; //存储桶地域
                preSignatureStruct.bucket = CosConfig.BucketName + "-" + CosConfig.appid; //存储桶
                preSignatureStruct.key = objectKey; //对象键
                preSignatureStruct.httpMethod = "GET"; //HTTP 请求方法
                preSignatureStruct.isHttps = false; //生成 HTTPS 请求 URL
                preSignatureStruct.signDurationSecond = 600; //请求签名时间为600s

                preSignatureStruct.queryParameters = null; //签名中需要校验的 URL 中请求参数

                var cosXml = InitServer();
                string requestSignURL = cosXml.GenerateSignURL(preSignatureStruct);
                return requestSignURL;


                //下载请求预签名 URL (使用永久密钥方式计算的签名 URL)
                string localDir = System.IO.Path.GetTempPath() + "/腾讯云存储";//本地文件夹

                if (!Directory.Exists(localDir)) Directory.CreateDirectory(localDir);

                string localFileName = objectKey; //指定本地保存的文件名
                //GetObjectRequest request = new GetObjectRequest(preSignatureStruct.bucket, objectKey, @"C:\Users\35024\Desktop", localFileName);


                //GetObjectRequest request = new GetObjectRequest(null,null, System.IO.Path.GetTempPath(), objectKey);

                GetObjectRequest request = new GetObjectRequest(null, null, localDir, objectKey);

                //设置下载请求预签名 URL
                //设置签名有效时长

                request.RequestURLWithSign = requestSignURL;
                //设置进度回调
                request.SetCosProgressCallback(delegate (long completed, long total)
                {
                    Console.WriteLine(String.Format("progress = {0:##.##}%", completed * 100.0 / total));
                });
                //执行请求
                GetObjectResult result = cosXml.GetObject(request);

                var resultInfo = result.GetResultInfo();
                //请求成功
                Console.WriteLine(result.GetResultInfo());
                return requestSignURL;
            }
            catch (COSXML.CosException.CosClientException clientEx)
            {
                //请求失败
                Console.WriteLine("CosClientException: " + clientEx);
                return "";
            }
            catch (COSXML.CosException.CosServerException serverEx)
            {
                //请求失败
                Console.WriteLine("CosServerException: " + serverEx.GetInfo());
                return "";

            }
        }


        public PutObjectResult PutObject(string objectkey, byte[] filedata)
        {
            var cosXml = InitServer();
            string bucket = CosConfig.BucketName + "-" + CosConfig.appid; //存储桶，格式：BucketName-APPID
            string cosPath = objectkey; // 对象键
            byte[] data = filedata; // 二进制数据
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucket, cosPath, data);
            PutObjectResult res = cosXml.PutObject(putObjectRequest);
            return res;
        }



        /// <summary>
        /// 上传时添加水印
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public ResponseObject AddWaterMarkWhenUpload(AddWaterMark model)
        {
            //需要先上传水印 至同一个bucket
            var cosXml = InitServer();
            string requestUrl = BuildRequestUrl(model.originImg.ObjectKey, "/" + DirectoryNames.Origin);
            //string requestUrl = $"https://{CosConfig.BucketName}-{CosConfig.appid}.cos.{"ap-beijing"}.myqcloud.com" + "/" + DirectoryNames.Origin + model.originImg.ObjectKey;
            var turple1 = BuildBasicParams(model, OpeartionType.Add);
            string finalKey = turple1.finalKey;
            string picoptions = "{\"is_pic_info\":1,\"rules\":[{\"fileid\":\"" + finalKey + "\",\"rule\":\" " + turple1.rule + "\"}]}";

            var headerDict = new Dictionary<string, string>();
            headerDict.Add("Pic-Operations", picoptions);
            string sign = cosXml.GenerateSign("put", "/" + DirectoryNames.Origin + model.originImg.ObjectKey, null, headerDict, 7000000);

            HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(requestUrl);
            myRequest.Method = "put";
            myRequest.Headers.Add("Authorization", sign);
            myRequest.Headers.Add("Pic-Operations", picoptions);
            byte[] uploadData = new byte[1024];
            uploadData = Convert.FromBase64String(model.originImg.FileData);

            //上传文件写入请求流
            var outstream = myRequest.GetRequestStream();
            outstream.Write(uploadData, 0, uploadData.Length);
            outstream.Flush();
            outstream.Close();

            return CommonRequest(myRequest, finalKey);


        }

        /// <summary>
        /// 下载时添加水印
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public Stream AddWaterMarkWhenDownLoad(AddWaterMark model)
        {

            string requestUrl = BuildRequestUrl(model.originImg.ObjectKey, "/");
            //$"https://{CosConfig.BucketName}-{CosConfig.appid}.cos.{"ap-beijing"}.myqcloud.com" + "/" + model.originImg.ObjectKey;

            var turple1 = BuildBasicParams(model, OpeartionType.Add);
            string rule = turple1.rule;


            string signUrl = GeneratePreviewUrl(model.originImg.ObjectKey);
            var sign = signUrl.Split('?')[1];

            requestUrl += "?" + rule + "&" + sign;

            return this.GetStream(requestUrl);

        }


        /// <summary>
        /// 从云端提取
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public ResponseObject ExtractFromCloud(AddWaterMark model)
        {
            //需要先上传水印 至同一个bucket
            var cosXml = InitServer();
            string requestUrl = BuildRequestUrl(model.processedImg.ObjectKey, "/");
            requestUrl += "?image_process";
            var turple1 = BuildBasicParams(model, OpeartionType.Extract);
            var headerDict = new Dictionary<string, string>();
            var queryDict = new Dictionary<string, string>();
            queryDict.Add("image_process", "");
            string picOptions = "{\"is_pic_info\":1,\"rules\":[{\"fileid\":\"" + turple1.finalKey + "\",\"rule\":\" " + turple1.rule + "\"}]}";
            headerDict.Add("Pic-Operations", picOptions);
            string sign = cosXml.GenerateSign("post", "/" + model.processedImg.ObjectKey, null, headerDict, 7000000);
            HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(requestUrl);
            myRequest.Method = "post";
            myRequest.Headers.Add("Authorization", sign);
            myRequest.Headers.Add("Pic-Operations", picOptions);

            return CommonRequest(myRequest, turple1.finalKey);
        }

        /// <summary>
        /// 上传后提取
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public ResponseObject ExtractFromUpload(AddWaterMark model)
        {
            //需要先上传水印 至同一个bucket
            var cosXml = InitServer();
            string requestUrl = BuildRequestUrl(model.processedImg.ObjectKey, "/" + DirectoryNames.Extract);

            var turple1 = BuildBasicParams(model, OpeartionType.Extract);

            var headerDict = new Dictionary<string, string>();
            string picOptions = "{\"is_pic_info\":1,\"rules\":[{\"fileid\":\"" + turple1.finalKey + "\",\"rule\":\" " + turple1.rule + "\"}]}";
            headerDict.Add("Pic-Operations", picOptions);
            string sign = cosXml.GenerateSign("put", "/" + DirectoryNames.Extract + model.processedImg.ObjectKey, null, headerDict, 7000000);
            HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(requestUrl);
            myRequest.Method = "put";
            myRequest.Headers.Add("Authorization", sign);
            myRequest.Headers.Add("Pic-Operations", picOptions);

            byte[] uploadData = Convert.FromBase64String(model.processedImg.FileData);
            var outstream = myRequest.GetRequestStream();
            outstream.Flush();
            outstream.Write(uploadData, 0, uploadData.Length);
            outstream.Flush();
            outstream.Close();

            return CommonRequest(myRequest, turple1.finalKey);

        }


        /// <summary>
        /// 获取图片元数据
        /// </summary>
        /// <param name="objectKey"></param>
        /// <returns></returns>
        public ImageInfoBase GetImageInfo(string objectKey)
        {
            string requestUrl = this.BuildRequestUrl(objectKey);

            string signUrl = GeneratePreviewUrl(objectKey);
            var sign = signUrl.Split('?')[1];
            requestUrl += "?" + "imageInfo" + "&" + sign;

            var stream = this.GetStream(requestUrl);
            System.IO.StreamReader reader = new System.IO.StreamReader(stream, Encoding.Default);
            var srcString = reader.ReadToEnd();
            return JsonConvert.DeserializeObject<ImageInfoBase>(srcString);
        }

        /// <summary>
        /// 清空bucket
        /// </summary>
        /// <returns></returns>
        public DeleteMultiObjectResult ClearBucket()
        {
            var cosXml = InitServer();
            DeleteMultiObjectRequest request = new DeleteMultiObjectRequest(CosConfig.BucketName);
            //设置返回结果形式
            request.SetDeleteQuiet(false);
            List<string> keys = new List<string>();
            ListBucket res = GetListFromBucket("", "1000");
            res.contentsList.ForEach(n => keys.Add(n.key));
            request.SetObjectKeys(keys);
            //执行请求
            DeleteMultiObjectResult result = cosXml.DeleteMultiObjects(request);
            return result;
        }

        /// <summary>
        /// 构建请求地址
        /// </summary>
        /// <param name="objectKey"></param>
        /// <param name="prefix"></param>
        /// <returns></returns>
        public string BuildRequestUrl(string objectKey, string prefix = "")
        {
            return $"https://{CosConfig.BucketName}-{CosConfig.appid}.cos.{"ap-beijing"}.myqcloud.com" + prefix + objectKey;
        }

        /// <summary>
        /// 构建基础参数
        /// </summary>
        /// <param name="model"></param>
        /// <param name="opeartionType"></param>
        /// <returns></returns>
        private (string finalKey, string rule) BuildBasicParams(AddWaterMark model, OpeartionType opeartionType)
        {
            string finalKey = "";
            string waterMarkParam = ((int)opeartionType).ToString();
            string directory = "";
            string rule = "";
            string urlSafeBase64 = "";
            //添加
            if (opeartionType == OpeartionType.Add)
            {
                if (model.waterMarkType != WaterMarkType.TextBlind)
                {
                    byte[] tempByte = System.Text.Encoding.UTF8.GetBytes(model.watermarkImg.PreviewUrl);
                    urlSafeBase64 = UrlSafeBase64(tempByte);
                }

                directory = "/" + DirectoryNames.Processed;

                string addFileName = model.originImg.ObjectKey;
                if (addFileName.Contains("/"))
                {
                    addFileName = addFileName.Substring(addFileName.LastIndexOf("/") + 1);
                }
                switch (model.waterMarkType)
                {
                    case WaterMarkType.HalfBlind:
                        {

                            finalKey = directory + "type1_" + addFileName;
                            rule = "watermark/" + waterMarkParam + "/type/1/image/" + urlSafeBase64;
                            //picOptions = "{\"is_pic_info\":1,\"rules\":[{\"fileid\":\"" + finalKey + "\",\"rule\":\"watermark/" + waterMarkParam + "/type/1/image/" + urlSafeBase64 + "\"}]}";
                            break;
                        }
                    case WaterMarkType.FullBlind:
                        {

                            finalKey = directory + "type2_" + addFileName;
                            rule = "watermark/" + waterMarkParam + "/type/2/image/" + urlSafeBase64 + "/level/" + model.level.ToString();
                            //picOptions = "{\"is_pic_info\":1,\"rules\":[{\"fileid\":\"" + finalKey + "\",\"rule\":\"watermark/+ waterMarkParam +/type/2/image/" + urlSafeBase64 + "/level/" + model.level.ToString() + "\"}]}";

                            break;
                        }
                    case WaterMarkType.TextBlind:
                        {
                            finalKey = directory + "type3_" + addFileName;
                            string base64markText = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(model.watermarktext));
                            rule = "watermark/" + waterMarkParam + "/type/3/text/" + base64markText;
                            break;
                        }

                }
            } 
            else if (opeartionType == OpeartionType.Extract)//提取
            {
                directory = "/" + DirectoryNames.Extract + DirectoryNames.Processed;

                string extractFileName = model.processedImg.ObjectKey;
                if (extractFileName.Contains("/"))
                {
                    extractFileName = extractFileName.Substring(extractFileName.LastIndexOf("/") + 1);
                }
                switch (model.waterMarkType)
                {

                    case WaterMarkType.HalfBlind: //提取 半盲水印 选择的是原图
                        {
                            finalKey = directory + "extracted_type1_" + extractFileName;
                            byte[] tempByte = System.Text.Encoding.UTF8.GetBytes(model.originImg.PreviewUrl);
                            urlSafeBase64 = UrlSafeBase64(tempByte);
                            rule = "watermark/" + waterMarkParam + "/type/1/image/" + urlSafeBase64;
                            //picOptions = "{\"is_pic_info\":1,\"rules\":[{\"fileid\":\"" + finalKey + "\",\"rule\":\"watermark/" + waterMarkParam + "/type/1/image/" + urlSafeBase64 + "\"}]}";
                            break;
                        }
                    case WaterMarkType.FullBlind:   //提取 全盲水印 选择的是水印图
                        {
                            finalKey = directory + "extracted_type2_" + extractFileName;
                            byte[] tempByte = System.Text.Encoding.UTF8.GetBytes(model.watermarkImg.PreviewUrl);
                            urlSafeBase64 = UrlSafeBase64(tempByte);
                            rule = "watermark/" + waterMarkParam + "/type/2/image/" + urlSafeBase64 + "/level/" + model.level.ToString();
                            //picOptions = "{\"is_pic_info\":1,\"rules\":[{\"fileid\":\"" + finalKey + "\",\"rule\":\"watermark/+ waterMarkParam +/type/2/image/" + urlSafeBase64 + "/level/" + model.level.ToString() + "\"}]}";

                            break;
                        }
                    case WaterMarkType.TextBlind: //提取 文字水印 （文档上写需要文字 经过测试不需要文字）
                        {
                            finalKey = directory + "extracted_type3_" + extractFileName;
                            //string base64markText = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(model.watermarktext));
                            //rule = "watermark/" + waterMarkParam + "/type/3/text/" + base64markText;
                            rule = "watermark/" + waterMarkParam + "/type/3/text";
                            break;
                        }

                }

            }


            //picOptions = "{\"is_pic_info\":1,\"rules\":[{\"fileid\":\"" + finalKey + "\",\"rule\":\" " + rule + "\"}]}";
            return (finalKey, rule);
        }

        /// <summary>
        /// 封装请求
        /// </summary>
        /// <param name="request"></param>
        /// <param name="finalKey"></param>
        /// <returns></returns>
        private ResponseObject CommonRequest(HttpWebRequest request, string finalKey)
        {
            var resObj = new ResponseObject();
            WebResponse webResponse = null;
            resObj.Success = true;
            try
            {
                webResponse = request.GetResponse();
            }
            catch (WebException ex)
            {
                resObj.Success = false;
                webResponse = (HttpWebResponse)ex.Response;
            }

            Stream stream = webResponse.GetResponseStream();
            System.IO.StreamReader reader = new System.IO.StreamReader(stream, Encoding.UTF8);
            var srcString = reader.ReadToEnd();
            Dictionary<string, string> responseHeaders = new Dictionary<string, string>();
            foreach (string key in webResponse.Headers.Keys)
            {
                responseHeaders.Add(key, webResponse.Headers.Get(key));
            }
            resObj.Header = JsonConvert.SerializeObject(responseHeaders);
            resObj.Body = srcString;
            resObj.ProcessResult = new { previewurl = GeneratePreviewUrl(finalKey), finalKey = finalKey };

            return resObj;
        }



        /// <summary>
        /// Get stream
        /// </summary>
        /// <param name="Url"></param>
        /// <returns></returns>
        public Stream GetStream(string Url)
        {

            WebResponse response = null;
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url);
            request.Method = "get";
            try
            {
                response = request.GetResponse();

            }
            catch (WebException ex)
            {
                response = (HttpWebResponse)ex.Response;
            }
            Stream stream = response.GetResponseStream();
            return stream;

        }

        private static string UrlSafeBase64(string source)
        {
            char[] padding = { '=' };
            byte[] tempByte = System.Text.Encoding.Default.GetBytes(source);
            string base64String = Convert.ToBase64String(tempByte);
            return base64String.Replace("+", "-")
                .Replace("/", "_")
                .Replace("=", "");
        }


        private static string UrlSafeBase64(byte[] tempByte)
        {
            string base64String = Convert.ToBase64String(tempByte);
            return base64String.Replace("+", "-")
                .Replace("/", "_")
                .Replace("=", "");
        }
    }
}